#ifndef _LIODEBUG_H
#define _LIODEBUG_H
#include "liotool.h"

#define CALL_INFO __FILE__,__LINE__,__FUNCSIG__
#define CALL_PARAM const char *file, long line, const char *func

#ifdef _DEBUG
	#define _DEBUG_EXPR(expr) expr
#else
	#define _DEBUG_EXPR(expr)
	#define _DISABLE_DEBUG_TOOLS
#endif

#define _DEBUG_PRINT

#ifndef _DISABLE_DEBUG_TOOLS
	#define _DEBUG_SRC
	#define _DEBUG_MEM
	#define _DEBUG_TEST
	#define _DEBUG_TIME
#endif

namespace Lio {

extern BOOL __debugger_present;

#ifdef _DEBUG_PRINT
	extern FILE *__debug_out,*__prev_debug_out;
	#define NPRINT(N,fmt,...){\
		TCHAR buff[N];_sntprintf(buff,N,fmt,__VA_ARGS__);\
		if(Lio::__debugger_present)::OutputDebugString(buff);\
		if(Lio::__debug_out)_ftprintf(Lio::__debug_out,buff);\
		_tprintf(buff);\
	}
	#define PRINT(fmt,...)NPRINT(10*1024,fmt,__VA_ARGS__)
	#define PRINT_VECTOR(fmt,v,n){for(int i=0;i<n;i++)PRINT(fmt,v[i]);}
	#define SET_DEBUG_OUT(fOutput){__prev_debug_out=__debug_out;__debug_out=(fOutput);}
	#define REM_DEBUG_OUT(fOutput){if(__debug_out==(fOutput))__debug_out=__prev_debug_out;}
	#define FLUSH_DEBUG_OUT()if(__debug_out)fflush(__debug_out);
	class TempDebugOutput
	{
		File file;
		FILE *handle;
	public:
		TempDebugOutput(FILE *file);
		TempDebugOutput(CSTR name, CSTR mode = _T("wt"));
		~TempDebugOutput();
	};
	#define TEMP_DEBUG_OUT(...) Lio::TempDebugOutput __tmp_debug_out(__VA_ARGS__);
#else
	#define __debug_out NULL
	#define NPRINT(N, fmt, ...)
	#define PRINT(fmt,...)
	#define PRINT_VECTOR(fmt,v,n)
	#define SET_DEBUG_OUT(fOutput)
	#define REM_DEBUG_OUT(fOutput)
	#define FLUSH_DEBUG_OUT()
	#define TEMP_DEBUG_OUT(...)
#endif

#ifdef _DEBUG_SRC
#	define SRC_LENGTH 64
	extern long __src_index,__src_cursor,__src_line[SRC_LENGTH];
	extern const char *__src_name[SRC_LENGTH], *__src_file[SRC_LENGTH];
#	define DECSRC(index) extern long index
#	define DEFSRC(func,index)long index=(__src_index=(__src_index+1)%SRC_LENGTH,__src_name[__src_index]=#func,__src_index)
#	define SETSRC(func,index)__src_cursor=index,assert(!__src_file[index]),__src_file[index]=__FILE__,	__src_line[index]=__LINE__,	func
	#define CLRSRC(func,index)__src_cursor=index,assert( __src_file[index]),__src_file[index]=NULL,		__src_line[index]=0,		func
#else
#	define DECSRC(index)
#	define DEFSRC(func,index)
#	define SETSRC(func,index)
#	define CLRSRC(func,index)
#endif

#ifdef _DEBUG_MEM
	#define ADDMEM(b,m)Lio::__mem_add(b,m,CALL_INFO)
#	define REMMEM(b)Lio::__mem_rem(b,CALL_INFO)
	
#	define PRINTMEM(...)Lio::__mem_dump(__VA_ARGS__)

#	define MAX_ALLOC_COUNT (64*1024)
	void __mem_add(const void *buff, unsigned long mem, CALL_PARAM);
	void __mem_rem(const void *buff, CALL_PARAM);
	void __mem_dump(BOOL verbose = FALSE);
	// ALLOC MEMORY:
#	define NEW_OBJECT(pointer,type,...) ((pointer=new(std::nothrow)type(__VA_ARGS__)),ADDMEM(pointer,sizeof(type)),pointer)
#	define NEW_ARRAY(pointer,type,count) ((pointer=new(std::nothrow)type[count]),ADDMEM(pointer,count*sizeof(type)),pointer)
#	define NEW_MEMORY(pointer,type,size) ((pointer=(type*)malloc(size)),ADDMEM(pointer,size),pointer)
#	define NEW_MEMORY0(pointer,type,count) ((pointer=(type*)calloc(count,sizeof(type))),ADDMEM(pointer,count*sizeof(type)),pointer)
#	define NEW_ALIGNED(pointer,type,size,align) ((pointer=(type*)_aligned_malloc((size),(align))),ADDMEM(pointer,size),pointer)
	// FREE MEMORY:
#	define DELETE_OBJECT(pointer) (REMMEM(pointer),delete (pointer))
#	define DELETE_ARRAY(pointer) (REMMEM(pointer),delete [] (pointer))
#	define DELETE_MEMORY(pointer) (REMMEM(pointer),free(pointer))
#	define DELETE_ALIGNED(pointer) (REMMEM(pointer),_aligned_free(pointer))
#else
#	define PRINTMEM(...)
	// ALLOC MEMORY:
#	define NEW_OBJECT(pointer,type,...) (pointer=new(std::nothrow)type(__VA_ARGS__))
#	define NEW_ARRAY(pointer,type,count) (pointer=new(std::nothrow)type[count])
#	define NEW_MEMORY(pointer,type,size) (pointer=(type*)malloc(size))
#	define NEW_MEMORY0(pointer,type,count) (pointer=(type*)calloc(count,sizeof(type)))
#	define NEW_ALIGNED(pointer,type,size,align) (pointer=(type*)_aligned_malloc((size),(align)))
	// FREE MEMORY:
#	define DELETE_OBJECT(pointer) delete (pointer) 
#	define DELETE_ARRAY(pointer) delete [] (pointer)
#	define DELETE_MEMORY(pointer) free(pointer)
#	define DELETE_ALIGNED(pointer) _aligned_free(pointer)
#endif

#ifdef _DEBUG_TIME
#	define MAX_TIMES 10
	void print_monitor_times();
	void start_monitor_time();
	void stop_monitor_time(int profile);
#	define PRINT_MONITOR_TIMES() print_monitor_times()
#	define START_MONITOR_TIME(profile) start_monitor_times(profile)
#	define STOP_MONITOR_TIME(profile) stop_monitor_times(profile)
#else
#	define PRINT_MONITOR_TIMES()
#	define START_MONITOR_TIME()
#	define STOP_MONITOR_TIME(profile)
#endif

#ifdef _DEBUG_TEST
#	define _TEST(expr) expr
#else
#	define _TEST(expr)
#endif

#define PRINTFUNC()PRINT(_T("Function %s(%d): %s\n"),CALL_INFO)

int __exception(struct _EXCEPTION_POINTERS *pEP);
#define EXCEPTION_HANDLER __exception(GetExceptionInformation())

}; // namespace Lio

#endif //_LIODEBUG_H